package com.example.dragcontentviewdemo.helper.internal.touchhandler;

import android.animation.Animator;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;


import androidx.annotation.NonNull;

import com.example.dragcontentviewdemo.helper.internal.animationlistener.AnimationListenerImpl;
import com.example.dragcontentviewdemo.helper.internal.animationlistener.MoveBackAnimationUpdateListener;
import com.example.dragcontentviewdemo.helper.internal.animationlistener.MoveToRightAnimationUpdateListener;
import com.example.dragcontentviewdemo.helper.internal.connector.AttachInfo;
import com.example.dragcontentviewdemo.helper.internal.util.CaculatorUtil;

public class TouchHandler implements View.OnTouchListener{

    private static final int CONSTANT_MAX_COLOR_BIT_VALUE = 255; //RGB最大值
    private static final float CONSTANT_MIN_MOVE_DISTANCE = 100.f;
    private static final String TAG = "TouchHandler";

    AttachInfo mAttachInfo = null;
    Animator.AnimatorListener animationListenerForMoveOutOfBound = null;
    Animator.AnimatorListener animationListenerForMoveBack = null;

    MoveBackAnimationUpdateListener mAnimatorUpdateListenerForMoveBackOperation;
    MoveToRightAnimationUpdateListener mAnimatorUpdateListenerForMoveToRightOperation;

    public TouchHandler(@NonNull AttachInfo attachInfo){
        mAttachInfo = attachInfo;
        mAnimatorUpdateListenerForMoveBackOperation = new MoveBackAnimationUpdateListener(mAttachInfo.mContentView,mAttachInfo);
        mAnimatorUpdateListenerForMoveToRightOperation = new MoveToRightAnimationUpdateListener(mAttachInfo.mContentView,mAttachInfo);

        animationListenerForMoveOutOfBound = new AnimationListenerImpl(true,mAttachInfo);
        animationListenerForMoveBack = new AnimationListenerImpl(false,mAttachInfo);
    }

    private void log(String msg){
        Log.d(TAG, msg);
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        /*
            需要获取contentView的MeasureWidth， 判断起点坐标是否在view的左侧开始+x:5w-30w的位置
            在move过程移动view的位置
            在up结束时判断view的位移量x，
        */
        boolean result = false;
        final boolean skip = resetCancelNextUpFlag(mAttachInfo);

        if( skip || !mAttachInfo.mIntercept || mAttachInfo.mAnimating){
            return false; //动画中，不处理, result为false后该事件将交由v.onTouchEvent处理，包括执行点击与长按
        }

        View contentView = mAttachInfo.mContentView; //实际移动的View
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                mAttachInfo.mFirstDownEvent = MotionEvent.obtain(event);
                result = handleActionDown(event, contentView);
                break;
            case MotionEvent.ACTION_MOVE:
                result = handleActionMove(event, contentView);
                break;
            case MotionEvent.ACTION_UP:
                result = handleActionUp(event, contentView);
                break;
            case MotionEvent.ACTION_CANCEL:
                result = handleActionCancel(event, contentView);
                break;
        }
        return result;
    }

    private boolean handleActionCancel(MotionEvent event,View v){
        log("action cancel");
        mAttachInfo.resetState();
        return false; //手势被取消，意味着unhandled;
    }

    private boolean handleActionDown(MotionEvent event,View v){
        //响应一个手指down
        if (event.getPointerCount() == 1) {
            float down_x = event.getRawX();
            mAttachInfo.mActionStarted = true;
            mAttachInfo.mFirstX = down_x;
            mAttachInfo.mLastX = down_x;
            log("action down");
            return true;
        }
        return false; //unhandled
    }

    private boolean handleActionUp(MotionEvent event,View v){
        //加一层判断出： ACTION_DOWN->ACTION_UP ， 不做处理
        if(!mAttachInfo.mViewIsMoved){
            return false; //unhandled;
        }
        mAttachInfo.mVelocityTracker.addMovement(event);
        /*
        需要根据给定的速度阈值与当前位置判断应该对view的偏移做出何种反应。
         */
        if( v.getTranslationX() > v.getWidth() / 2.0) {
            //位置超过一半，此时不需要加入速度判断
            doMoveToRightOperation(v);
        }
        else {
            //位置未超过一半，需要加入速度与方向判断;
            //计算速度
            mAttachInfo.mVelocityTracker.computeCurrentVelocity(1000);
            float velocityX = mAttachInfo.mVelocityTracker.getXVelocity();

            log("velocityX"+velocityX+"\tdirection: "+ mAttachInfo.mDirection);
            if(mAttachInfo.mDirection && velocityX > mAttachInfo.mVelocityXThreshold){
                doMoveToRightOperation(v);
            }
            else{
                doMoveBackOperation(v);
            }
        }

        log("action up");
        mAttachInfo.resetState();
        return true;
    }

    private boolean handleActionMove(MotionEvent event,View v){
        if(mAttachInfo.mActionStarted){
            //加入精度过滤， 只需要过滤一次
            if ( mAttachInfo.mNeedPrecisionFilter ){
                if ( mAttachInfo.mFirstDownEvent != null && CaculatorUtil.distance(mAttachInfo.mFirstDownEvent, event) > CONSTANT_MIN_MOVE_DISTANCE) {
                    mAttachInfo.mNeedPrecisionFilter = false;
                    //真正的down从这里开始
                    handleActionDown(event, v);
                }
                else{
                    return false; //距离不够，退出
                }
            }

            mAttachInfo.mViewIsMoved = true;
            mAttachInfo.mVelocityTracker.addMovement(event);
            float ev_x = event.getRawX();

            mAttachInfo.mDirection = ev_x - mAttachInfo.mLastX > 0;
            mAttachInfo.mLastX = ev_x;

            /**
             * 解决issue: 先左滑后右滑再快速左滑时将会出现无法归位的bug
             * 动作事件的采集点变宽，在offsetX=0之前就计算出了ev_x - first_x < 0 这是需要判断是否需要做强制归位
             */
            /**
             * 新增从视图外滑动回来的逻辑
             */
            float offset = 0.f;

            if ( mAttachInfo.mIsViewHiddenNow ) {
                //类似不允许左滑出边界的处理
                offset = Math.min(v.getWidth() + ev_x - mAttachInfo.mFirstX, v.getWidth());
                if ( mAttachInfo.callback != null ) {
                    final float fraction = CaculatorUtil.calculateTranslationXFraction(v);
                    mAttachInfo.callback.onMoved(fraction,mAttachInfo.mDirection);
                }
            }
            else {
                //不允许左滑出边界
                offset = Math.max(0, ev_x - mAttachInfo.mFirstX);
                if ( mAttachInfo.callback != null) {
                    float fraction = CaculatorUtil.calculateTranslationXFraction(v);
                    mAttachInfo.callback.onMoved(fraction,mAttachInfo.mDirection);
                }
            }

            //计算offset,这里offset总是大于等于0
            v.setTranslationX(offset);
            log("ev_x: "+ ev_x);

            //计算透明度
            int alpha = CONSTANT_MAX_COLOR_BIT_VALUE - (int) (CONSTANT_MAX_COLOR_BIT_VALUE * ( (offset) / v.getWidth() ) );
            log("alpha: "+ alpha);

            if(mAttachInfo.callback != null) {
                float fraction = CaculatorUtil.calculateTranslationXFraction(v);
                mAttachInfo.callback.onMoved(fraction, mAttachInfo.mDirection);
            }

            //调整背景
            Drawable bg = mAttachInfo.mDecorView.getBackground();
            bg.setAlpha(alpha);
            mAttachInfo.mDecorView.invalidateDrawable(bg);

            return true;
        }
        return false;
    }

    private void doMoveToRightOperation(View view){
        mAnimatorUpdateListenerForMoveToRightOperation.updateCurrAlpha();
        view.animate()
                .setUpdateListener(mAnimatorUpdateListenerForMoveToRightOperation)
                .translationX(view.getWidth()+1)
                .setListener(animationListenerForMoveOutOfBound); //这里应该在过渡动画结束后退出activity
    }

    private void doMoveBackOperation(View view){
        //这里的updateListener可能已经设置过了，这里会有重复设置的情况
        mAnimatorUpdateListenerForMoveBackOperation.updateCurrAlpha();
        view.animate().setUpdateListener(mAnimatorUpdateListenerForMoveBackOperation);
        view.animate().translationX(0).setListener(animationListenerForMoveBack);
    }


    /**
     * 重置分离开关
     * @param attachInfo
     * @return 返回true 如果先前设置过此值， 否则返回false
     */
    private static boolean resetCancelNextUpFlag(AttachInfo attachInfo){
        if ( attachInfo.mCancelNextUp ){
            attachInfo.mCancelNextUp = false;
            return true;
        }
        return false;
    }



}